package com.jy.appmarket.network.engine;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.Map;
import java.util.TreeMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import android.content.Context;
import android.graphics.Point;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.view.WindowManager;

import com.jy.appmarket.AppMarketApplication;
import com.jy.appmarket.network.engine.cachestrategy.CacheStrategy;
import com.jy.appmarket.network.resultdata.BaseResultData;
import com.jy.appmarket.utils.FileManager;
import com.jy.appmarket.utils.LocationUtil;
import com.jy.appmarket.utils.LogUtil;
import com.jy.appmarket.utils.NetTools;

/**
 * 类说明：
 * 
 * @author Terry.Lu
 * @date 2013-5-15
 * @version 1.0
 */
public abstract class BaseNetEngine {
	public static final int HTTP_GET = 0;
	public static final int HTTP_POST = 1;
	public static final int HTTP_POSTBODY = 2;

	private static final String TAG = "BaseNetEngine";
	/** 1 minute */
	private static final int TIME_OUT = 60 * 1000;

	protected int mHttpMethod = HTTP_GET;
	protected boolean mIsConnectToOurServer = true;
	protected BaseResultData mResultData = null;
	protected CacheStrategy mCacheStrategy = null;
	private HttpURLConnection mHttpConn = null;
	private boolean mIsGetNetDataCanceled = false;
	private static final String VERIFY_CONTENT_SOURCE = "www.sbx.com";

	/**
	 * 获取联网后的结果； 子类可以通过重载本函数实现返回不同的结果，比如在违章查询的时候，可能的结果有4种；
	 * 一般需要同时重载getNetData函数，来配合使用，以返回不同的结果
	 */
	public BaseResultData getResultData() {
		return mResultData;
	}

	/**
	 * 取消联网获取数据
	 */
	public void cancel() {
		mIsGetNetDataCanceled = true;
		if (mResultData != null) {
		}
	}

	/**
	 * 设置缓存规则，默认规则为没有缓存
	 */
	public void setCacheStrategy(CacheStrategy strategy) {
		mCacheStrategy = strategy;
	}

	/**
	 * 获取网络数据
	 */
	public boolean getNetData(Context c) {
		if (mCacheStrategy == null) {
			mCacheStrategy = new CacheStrategy(false);
		}

		FileManager.createFileDir();

		if (mResultData == null) {
			throw new RuntimeException("mResultData is null, you should init it first");
		}

		mIsGetNetDataCanceled = false;
		String requestUrl = getHttpUrl(c) + "?" + getParamsData(c, null);

		if (mCacheStrategy.isCacheable() && mCacheStrategy.getUrl() == null) {
			mCacheStrategy.setUrl(requestUrl);
		}

		InputStream is = null;
		try {
			if (mCacheStrategy.isCacheable()) {
				if (mCacheStrategy.isNeedToDoNetWork()) {
					if ((is = doHttpDownload(c)) == null) {
						return false;
					}

					mCacheStrategy.saveContentToCacheFile(is);
					is = mCacheStrategy.getInputStreamFromCahceFile();
				} else {
					is = mCacheStrategy.getInputStreamFromCahceFile();
				}
			} else {
				is = doHttpDownload(c);
			}

			if (is == null) {
				return false;
			}

			if (mIsGetNetDataCanceled) {
				return false;
			}
			byte[] bytes = new byte[1024 * 10];
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			int len = 0;
			while ((len = is.read(bytes)) != -1) {
				baos.write(bytes, 0, len);
				if (mIsGetNetDataCanceled) {
					return false;
				}
			}
			is.close();
			String result = new String(baos.toByteArray(), "UTF-8");

			// LogUtil.i(TAG, "parse data..");
			if (!mResultData.parseData(result)) {
				LogUtil.e(TAG, "parse result error " + getCommand() + " msg:" + mResultData.getMessage());
				// 解析错误后，则删除文件，防止服务器出错后，一直无法正确解析
				mCacheStrategy.deleteCahceFile();
				return false;
			}

			return true;
		} catch (Exception e) {
			LogUtil.w(TAG, "error happened:", e);
			return false;
		}
	}

	protected abstract String getCommand();

	protected String getHttpUrl(Context c) {
		String command = getCommand();

		if (command == null) {
			LogUtil.w(TAG, "no command");
			return "";
		}

		String url = NetTools.getServerUrl();
		return url + command;
	}

	/**
	 * GET参数，除了固定参数和签名数据以外的其他参数
	 * 
	 */
	protected void getParams(Context context, Map<String, String> params) {

	}

	protected void postParams(Context context, Map<String, String> params) {

	}

	private void appendCommonParams(Context context, Map<String, String> params) {
		String android_id = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
		params.put("_aid", android_id);// AndroidID
		TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
		String device_id = tm.getDeviceId();
		// if(tm.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA){
		// params.put(key, value)
		// }else{
		// }
		// IMEI (_aid, _mid 至少要传一个)
		params.put("_mid", device_id);
		try {
			params.put("_sop", tm.getSimOperator());
		} catch (Exception e) {
			// ignore
		}
		// 版本名，必填.格式为：n.m.x, 比如：1.3.2
		params.put("_vn", NetTools.getVersionName(context));
		// 版本号，必填。比如：11
		params.put("_vc", String.valueOf(NetTools.getVersionCode(context)));
		params.put("_ch", NetTools.getChannelNumber(context));
		params.put("_pn", context.getPackageName());
		// IMSI
		params.put("_sid", tm.getSubscriberId());
		// magicNum
		params.put("_mnum", NetTools.getMagicNum(context));

		// 添加userId
		if (AppMarketApplication.isLogin()) {
			params.put("userId", AppMarketApplication.getLogin().getUserId());
		}
	}

	private String createChecksignString(final TreeMap<String, String> params,
			final TreeMap<String, String> postParams) {
		if (params == null) {
			return "";
		}

		TreeMap<String, String> dest = params;

		if (mHttpMethod == HTTP_POST && postParams != null) {
			dest = new TreeMap<String, String>(params);
			dest.putAll(postParams);
		}

		StringBuilder sb = new StringBuilder();
		for (String key : dest.keySet()) {
			String value = dest.get(key);
			if (value != null) {
				sb.append(value);
			}
			sb.append("&");
		}
		sb.append(NetTools.getSecrectKey()).append('&').append(getCommand());
		return sb.toString();
	}

	protected String getParamsData(Context context, TreeMap<String, String> postParams) {
		TreeMap<String, String> paramsMap = new TreeMap<String, String>();
		appendCommonParams(context, paramsMap);
		final TreeMap<String, String> custParams = new TreeMap<String, String>();
		getParams(context, custParams);
		if (!custParams.isEmpty()) {
			paramsMap.putAll(custParams);
		}
		String checkSign = createChecksignString(paramsMap, postParams);
		String sign = md5(checkSign);

		StringBuilder sb = new StringBuilder();
		for (String key : paramsMap.keySet()) {
			try {
				String k = URLEncoder.encode(key, "UTF-8");
				String v = "";
				if (!TextUtils.isEmpty(paramsMap.get(key))) {
					v = URLEncoder.encode(paramsMap.get(key), "UTF-8");
				}
				sb.append(k).append('=').append(v).append('&');
			} catch (UnsupportedEncodingException e) {
				// not gonna happen
			}
		}
		sb.append("_cs").append('=').append(sign);
		return sb.toString();
	}

	protected void setCustomHeaders(Context context, HttpURLConnection mHttpConn) {
		if (context == null || mHttpConn == null) {
			LogUtil.w(TAG, "context or conn is null");
			return;
		}
		StringBuilder info = new StringBuilder();
		info.append("_bd=").append(NetTools.urlEncode(Build.BRAND));
		info.append("&_md=").append(NetTools.urlEncode(Build.MODEL));

		ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo ni = cm.getActiveNetworkInfo();
		String net_type = "";
		if (ni != null) {
			switch (ni.getType()) {
			case ConnectivityManager.TYPE_WIFI:
				net_type = "wifi";
				break;
			case ConnectivityManager.TYPE_MOBILE:
				net_type = "mobile";
				break;
			default:
				net_type = "";
			}
		}
		info.append("&_nw=").append(net_type);
		info.append("&_ts=").append(System.currentTimeMillis());

		try {
			WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
			Point pt = new Point();
			wm.getDefaultDisplay().getSize(pt);
			String _wh = pt.x + "*" + pt.y;
			info.append("&_wh=").append(_wh);
		} catch (Exception e) {
			// ignore todo
		}
		// longitude (_lg) ---- 经度
		// latitude (_lt) ---- 纬度
		// location (_lc) ---- 地理位置（如： 深圳）

		LocationUtil lu = LocationUtil.getInstance(context);

		String longitude = NetTools.urlEncode(lu.getLongitude());
		String latitude = NetTools.urlEncode(lu.getLatitude());
		String city = NetTools.urlEncode(lu.getCity());
		if (!TextUtils.isEmpty(longitude)) {
			info.append("&_lg=").append(longitude);
		}
		if (!TextUtils.isEmpty(latitude)) {
			info.append("&_lt=").append(latitude);
		}

		if (!TextUtils.isEmpty(city)) {
			info.append("&_lc=").append(city);
		}

		mHttpConn.addRequestProperty("X-JiaoYan-Device", info.toString());
	}

	protected String getContentType() {
		return "application/x-www-form-urlencoded;charset=UTF-8";
	}

	/**
	 * POST body
	 */
	protected byte[] getBody(Context context) {
		return null;
	}

	protected InputStream httpGet(Context c) {
		URL url = null;
		int retryTimes = 3;
		String requestUrl = getHttpUrl(c);
		if (requestUrl == null) {
			return null;
		}

		requestUrl += "?" + getParamsData(c, null);
		LogUtil.i(TAG, "httpGet requesturl:" + requestUrl);
		try {
			while (retryTimes > 0) {
				url = new URL(requestUrl);
				if (NetTools.isCmwap(c)) {
					// LogUtil.i(TAG, "use cmwap...");
					mHttpConn = NetTools.getCmwapConnect(requestUrl);
				} else {
					mHttpConn = (HttpURLConnection) url.openConnection();
				}

				mHttpConn.setConnectTimeout(TIME_OUT);
				mHttpConn.setReadTimeout(TIME_OUT);
				NetTools.setCommonHttpHeader(mHttpConn);
				setCustomHeaders(c, mHttpConn);
				mHttpConn.setRequestProperty("Accept-Encoding", "gzip, deflate");
				mHttpConn.setRequestMethod("GET");

				int statusCode = mHttpConn.getResponseCode();
				if (statusCode != 200) {
					LogUtil.w(TAG, "httpGet statusCode error:" + statusCode);
					return null;
				}

				Map<String, String> header = NetTools.getHttpResponseHeader(mHttpConn);

				// 确定是自己的服务器下发的内容(有可能在使用CMWAP的时候出现移动服务器下发错误的内容)
//				if (mIsConnectToOurServer) {
//					String source = header.get("content-source");
//					if (source == null || !source.equals(VERIFY_CONTENT_SOURCE)) {
//						LogUtil.i(TAG, "content-source error" + source + ".Retry:" + retryTimes);
//						retryTimes--;
//						Thread.sleep(1000);
//						continue;
//					}
//				}

				InputStream is = mHttpConn.getInputStream();
				String gZipValue = header.get("content-encoding");
				if (gZipValue != null && gZipValue.indexOf("gzip") >= 0) {
					// LogUtil.i(TAG, "httpGet Gzip Stream");
					is = new GZIPInputStream(is);
				}
				return is;
			}

		} catch (Exception e) {
			LogUtil.w(TAG, "httpGet error:", e);
		}
		return null;
	}

	protected InputStream httpPost(Context context) {
		URL url = null;
		int retryTimes = 3;
		String requestUrl = getHttpUrl(context);
		if (requestUrl == null) {
			return null;
		}
		final TreeMap<String, String> postParams = new TreeMap<String, String>();
		postParams(context, postParams);
		requestUrl += "?" + getParamsData(context, postParams);
		LogUtil.i(TAG, "httpPost requesturl:" + requestUrl);
		try {
			while (retryTimes > 0) {
				url = new URL(requestUrl);
				if (NetTools.isCmwap(context)) {
					mHttpConn = NetTools.getCmwapConnect(requestUrl);
				} else {
					mHttpConn = (HttpURLConnection) url.openConnection();
				}

				mHttpConn.setDoOutput(true);
				mHttpConn.setDoInput(true);
				mHttpConn.setConnectTimeout(TIME_OUT);
				mHttpConn.setReadTimeout(TIME_OUT);
				mHttpConn.setRequestMethod("POST");
				NetTools.setCommonHttpHeader(mHttpConn);
				mHttpConn.setRequestProperty("Accept-Encoding", "gzip, deflate");
				mHttpConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
				if (postParams != null && !postParams.isEmpty()) {
					StringBuilder sb = new StringBuilder();
					for (String key : postParams.keySet()) {
						String k = NetTools.urlEncode(key);
						String v = NetTools.urlEncode(postParams.get(key));
						sb.append(k).append('=').append(v).append('&');
					}
					sb.deleteCharAt(sb.length() - 1);
					OutputStream out = mHttpConn.getOutputStream();
					out.write(sb.toString().getBytes("UTF-8"));
					out.flush();
					out.close();
				}

				int statusCode = mHttpConn.getResponseCode();
				if (statusCode != 200) {
					LogUtil.w(TAG, "httpPost statusCode error:" + statusCode);
					return null;
				}

				Map<String, String> header = NetTools.getHttpResponseHeader(mHttpConn);
//				if (mIsConnectToOurServer) {
//					String source = header.get("content-source");
//					if (source == null || !source.equals(VERIFY_CONTENT_SOURCE)) {
//						LogUtil.d(TAG, "content-source:" + source);
//						LogUtil.i(TAG, "content-source is error and retry time=" + retryTimes);
//						retryTimes--;
//						Thread.sleep(1000);
//						continue;
//					}
//				}

				InputStream is = mHttpConn.getInputStream();
				String gZipValue = header.get("content-encoding");
				if (gZipValue != null && gZipValue.indexOf("gzip") >= 0) {
					LogUtil.i(TAG, "httpPost Gzip Stream");
					is = new GZIPInputStream(is);
				}
				return is;
			}

		} catch (Exception e) {
			// e.printStackTrace();
			LogUtil.w(TAG, "httpPost error:", e);
		}
		return null;
	}

	protected InputStream httpPostBody(Context context) {
		int retryTimes = 3;
		String requestUrl = getHttpUrl(context);
		if (requestUrl == null) {
			return null;
		}
		requestUrl += "?" + getParamsData(context, null);
		LogUtil.i(TAG, "httpPostBoundary requesturl:" + requestUrl);

		try {
			while (retryTimes > 0) {
				URL url = new URL(requestUrl);
				if (NetTools.isCmwap(context)) {
					mHttpConn = NetTools.getCmwapConnect(requestUrl);
				} else {
					mHttpConn = (HttpURLConnection) url.openConnection();
				}
				mHttpConn.setConnectTimeout(TIME_OUT);
				mHttpConn.setReadTimeout(TIME_OUT);
				mHttpConn.setDoInput(true);
				mHttpConn.setDoOutput(true);
				mHttpConn.setUseCaches(false);
				mHttpConn.setRequestMethod("POST");
				mHttpConn.setRequestProperty("connection", "keep-alive");
				mHttpConn.setRequestProperty("Content-Type", getContentType());
				mHttpConn.setRequestProperty("Accept-Charset", "GBK,utf-8;q=0.7,*;q=0.3");
				mHttpConn.setRequestProperty("Accept-Encoding", "gzip,deflate,sdch");
				mHttpConn.setRequestProperty("Accept",
						"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
				mHttpConn.setRequestProperty("User-Agent",
						"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 ");

				byte[] bytes = getBody(context);
				if (bytes != null && bytes.length != 0) {
					mHttpConn.setRequestProperty("Content-Encoding", "gzip");
					GZIPOutputStream gzipOut = new GZIPOutputStream(mHttpConn.getOutputStream());
					gzipOut.write(bytes);
					gzipOut.finish();
				}

				int statusCode = mHttpConn.getResponseCode();
				if (statusCode != 200) {
					LogUtil.w(TAG, "httpPostBoundary statusCode error:" + statusCode);
					return null;
				}
				Map<String, String> header = NetTools.getHttpResponseHeader(mHttpConn);

				if (mIsConnectToOurServer) {
					String source = header.get("content-source");
					if (!VERIFY_CONTENT_SOURCE.equals(source)) {
						LogUtil.d(TAG, "content-source:" + source);
						LogUtil.i(TAG, "content-source is error and retry time=" + retryTimes);
						retryTimes--;
						Thread.sleep(1000);
						continue;
					}
				}

				InputStream is = mHttpConn.getInputStream();
				String gZipValue = header.get("content-encoding");

				if (gZipValue != null && gZipValue.indexOf("gzip") >= 0) {
					LogUtil.i(TAG, "httpPostBoundary Gzip Stream");
					is = new GZIPInputStream(is);
				}

				return is;
			}

		} catch (Exception e) {
			// e.printStackTrace();
			LogUtil.w(TAG, "httpPostBoundary error:", e);
		}

		return null;
	}

	protected final String md5(String str) {
		if (str == null) {
			return null;
		}
		return md5(str, "utf-8");
	}

	private String md5(String str, String encodingType) {
		MessageDigest md5 = null;
		try {
			md5 = MessageDigest.getInstance("MD5");
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

		try {
			md5.update(str.getBytes(encodingType));
		} catch (UnsupportedEncodingException e) {
			md5.update(str.getBytes());
		}

		byte[] md5Bytes = md5.digest();

		StringBuffer hexValue = new StringBuffer();
		for (int i = 0; i < md5Bytes.length; i++) {
			int val = ((int) md5Bytes[i]) & 0xff;
			if (val < 16) {
				hexValue.append("0");
			}
			hexValue.append(Integer.toHexString(val));
		}
		return hexValue.toString();
	}

	private InputStream doHttpDownload(Context c) {
		// LogUtil.d(TAG, "connect server " + getCommand());
		InputStream is = null;
		if (mHttpMethod == HTTP_GET) {
			is = httpGet(c);
		} else if (mHttpMethod == HTTP_POST) {
			is = httpPost(c);
		} else {
			is = httpPostBody(c);
		}
		return is;
	}

	protected String appendUrlParam(String url, String key, String value) {
		if (!TextUtils.isEmpty(value)) {
			if (url.indexOf("?") > 0) {
				url += "&" + key + "=" + value;
			} else {
				url += "?" + key + "=" + value;
			}
		}
		return url;
	}
}
